
package com.example.easyexcel.config;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.StringUtils;
import com.example.easyexcel.domain.DynamicFields;
import com.example.easyexcel.service.DynamicFieldsService;
import com.example.easyexcel.utils.Convert;
import com.example.easyexcel.utils.DateUtils;
import com.example.easyexcel.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.DateUtil;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Slf4j
public class NonClazzOrientedListener<T> implements ReadListener<Map<Integer, String>> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 缓存的数据
     */
    private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private final Map<Integer, String> showHeadMap = new HashMap<>();

    /**
     * 调用持久层批量保存
     */
    private final Consumer<List<T>> consumer;
    private final String tableName;
    private final Class<T> beanClass;

    public NonClazzOrientedListener(Consumer<List<T>> consumer, String tableName, Class<T> beanClass) {
        this.consumer = consumer;
        this.tableName = tableName;
        this.beanClass = beanClass;
    }

    @Override
    public void invoke(Map<Integer, String> row, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", row);
        //将row根绝 showHeadMap(允许的表头) 和 beanClass 转为对应的实体类
        T obj;
        try {
            obj = beanClass.newInstance();
            for (Integer k : row.keySet()) {
                Object v = row.get(k);
                if (showHeadMap.containsKey(k)) {
                    String attrName = showHeadMap.get(k);
                    Field field = beanClass.getDeclaredField(attrName);
                    field.setAccessible(true);
                    Object var = Convert.getFieldValueByReflect(field, v);//类型转换
                    field.set(obj, var);
                }
            }
        } catch (Exception e) {
            log.error("map解析为实体类[{}]失败", beanClass.toString(), e);
            throw new RuntimeException("导入失败", e);
        }
        cachedDataList.add(obj);

        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            try {
                // 执行具体消费逻辑
                consumer.accept(cachedDataList);
            } catch (Exception e) {
                log.error("Failed to upload data!data={}", cachedDataList);
                throw new RuntimeException("导入失败");
            }
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (cachedDataList != null && cachedDataList.size() != 0) {
            try {
                // 执行具体消费逻辑
                consumer.accept(cachedDataList);
                log.info("所有数据解析完成！");
            } catch (Exception e) {
                log.error("Failed to upload data!data={}", cachedDataList, e);
                throw new RuntimeException("导入失败");
            }
        }
    }

    /**
     * 这里会一行行的返回头
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        // 如果想转成成 Map<Integer,String>
        // 方案1： 不要implements ReadListener 而是 extends AnalysisEventListener
        // 方案2： 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换
        //该方法必然会在读取数据之前进行
        Map<Integer, String> columMap = ConverterUtils.convertToStringMap(headMap, context);
        log.info("解析到一条头数据:{}", columMap);
        //通过数据交互拿到这个表的表头
        DynamicFieldsService dynamicFieldsService = SpringUtils.getBean(DynamicFieldsService.class);
        // 从配置获取需要导出的字段（例如：数据库、配置文件或请求参数）
        List<DynamicFields> dynamicFieldsList = dynamicFieldsService.getDataWithShow(tableName);
        Map<String, String> attrNameDescMap = dynamicFieldsList.stream().collect(Collectors.toMap(DynamicFields::getAttrDesc, DynamicFields::getAttrName));
        //过滤到了只存在表里面的数据
        columMap.forEach((key, value) -> {
            if (attrNameDescMap.containsKey(value)) {
                showHeadMap.put(key, attrNameDescMap.get(value));
            }
        });
    }


}